package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.common.StringUtil;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    /**
     * 保存声音
     *
     * @param trackInfoVo
     * @param userId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
        String content = trackInfoVo.getTrackTitle() + trackInfoVo.getTrackIntro();
        String suggest = vodService.scanContent(content);
        if ("block".equals(suggest) || "review".equals(suggest)) {
            throw new RuntimeException("内容含有敏感信息,请修改！");
        }

        //1.根据专辑id查询专辑信息
        Long albumId = trackInfoVo.getAlbumId();
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //可以使用断言来代替下面这个代码
        Assert.notNull(albumInfo, "专辑:{}不存在！", albumId);
//        if (albumInfo == null) {
//            throw new GuiguException(500, "专辑不存在！");
//        }

        //2.保存声音信息
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        //2.1 设置用户id
        trackInfo.setUserId(userId);
        //2.2 设置声音序号
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        //2.3 设置声音来源
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        //2.4 设置审核状态：0501-审核通过 0502"-审核不通过 0503-审核中
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
        //2.5 TODO 调用腾讯点播平台获取音频文件时长、大小、类型。
        TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        }

        //2.6 判断是否提交封面图片
        if (StringUtil.isEmpty(trackInfo.getCoverUrl())) {
            //没有提交，就采用专辑封面图片
            trackInfo.setCoverUrl(albumInfo.getCoverUrl());
        }

        //注意别忘了将改变后的数据插入到数据库中，否则主键id找不到
        //这一次我在这个地方出了错。需要注意，下次需要记住必须插入数据，否则没有主键id
        trackInfoMapper.insert(trackInfo);

        //3.更新专辑表
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);

        //4.保存声音统计记录
        // 声音统计 0701-播放量 0702-收藏量 0703-点赞量 0704-评论数
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY, 0);
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT, 0);
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE, 0);
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT, 0);

        //5.TODO 调用腾讯点播平台发起音频内容异步审核任务。
        //5.1 发起异步审核任务，获取审核任务id
        String reviewTaskId = vodService.startAuditReviewTask((trackInfo.getMediaFileId()));
        if (StringUtils.isNotBlank(reviewTaskId)) {
            //5.2 将审核任务id关联到声音表中，后续采用定时任务查询审核任务结果
            trackInfo.setReviewTaskId(reviewTaskId);
            //5.3 将声音审核状态改为：审核中
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
            trackInfoMapper.updateById(trackInfo);
        }

    }

    /**
     * 保存声音统计信息
     *
     * @param trackId  声音id
     * @param statType 0701-播放量 0702-收藏量 0703-点赞量 0704-评论数
     * @param statNum  统计数目
     */
    @Override
    public void saveTrackStat(Long trackId, String statType, Integer statNum) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(statNum);

        trackStatMapper.insert(trackStat);

    }

    /**
     * 修改声音信息
     *
     * @param trackInfo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackInfo(TrackInfo trackInfo) {
        String content = trackInfo.getTrackTitle() + trackInfo.getTrackIntro();
        String suggest = vodService.scanContent(content);
        if ("block".equals(suggest) || "review".equals(suggest)) {
            throw new RuntimeException("内容含有敏感信息,请修改！");
        }


        //1.根据声音id查询原有的声音信息
        TrackInfo trackInfoOld = trackInfoMapper.selectById(trackInfo.getId());

        //2.判断声音标识是否需要更新
        if (!trackInfoOld.getMediaFileId().equals(trackInfo.getMediaFileId())) {
            //2.1 如果声音发生变更，则再次获取最新音频消息
            TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfo.getMediaFileId());
            if (mediaInfo != null) {
                trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfo.getDuration()));
                trackInfo.setMediaSize(mediaInfo.getSize());
                trackInfo.setMediaType(mediaInfo.getType());

                //2.2 对修改后的音频文件再次发起审核
                String reviewTaskId = vodService.startAuditReviewTask(trackInfo.getMediaFileId());
                trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
                trackInfo.setReviewTaskId(reviewTaskId);
            }

            //2.2 TODO 删除原有音频文件，避免占用存储空间
            vodService.deleteMedia(trackInfoOld.getMediaFileId());
        }

        //3.更新声音信息
        trackInfoMapper.updateById(trackInfo);

    }

    /**
     * 删除声音信息
     *
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long id) {
        //1.根据声音id查询声音记录
        TrackInfo trackInfo = trackInfoMapper.selectById(id);

        //2.根据专辑id查询专辑记录,并且声音数量要减一
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);

        //3.更新声音表序号，将大于被删除的声音序号的其他声音记录减一
        /*
        update track_info set track_num = track_num - 1
        where album_id = #{albumId}
            and order_num > #{orderNum}
         */
        //设置更新条件
        LambdaUpdateWrapper<TrackInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId())
                .gt(TrackInfo::getOrderNum, trackInfo.getOrderNum())
                .setSql("order_num = order_num - 1");
        //执行更新操作
        trackInfoMapper.update(null, updateWrapper);

        //4.删除声音记录
        trackInfoMapper.deleteById(id);

        //5.删除声音统计记录
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>()
                .eq(TrackStat::getTrackId, id));

        //6.删除云点播平台的音频文件
        vodService.deleteMedia(trackInfo.getMediaFileId());
    }

    /**
     * 详情页面查询专辑包含声音列表-动态展示付费标识
     *
     * @param pageInfo
     * @param albumId
     * @param userId
     * @return
     */
    @Override
    public Page<AlbumTrackListVo> getAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long albumId, Long userId) {
        ///1.根据查询条件分页获取声音列表，isShowPaidMark=false，默认不显示付费标识、
        pageInfo = trackInfoMapper.getAlbumTrackPage(pageInfo, albumId);

        //2.TODO 动态判断是否显示付费标识，如果需要展示：将isShowPaidMark设置为true
        //3.根据专辑id查询专辑信息，得到专辑付费类型已经试听的声音数量。
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //3.1 获取专辑付费类型
        String payType = albumInfo.getPayType();
        //3.2 获取试听声音数量
        Integer tracksForFree = albumInfo.getTracksForFree();

        //4.判断当前用户是否登录-处理未登录的情况-付费类型属于：vip免费或者付费
        //付费类型: 0101-免费、0102-vip免费、0103-付费
        //未登录
        if (userId == null) {
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)
                    || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //4.1 情况一：遍历声音列表，将试听声音排除掉，将其他声音付费标识设置为true
                pageInfo.getRecords()
                        .stream()
                        .filter(track -> {
                            return track.getOrderNum() > tracksForFree;
                        })
                        .forEach(track -> track.setIsShowPaidMark(true));

            }

        } else {
            //已登录
            //5.TODO 处理已登录的情况，根据用户购买情况判断是否显示付费标识。
            //5.1 远程调用用户服务获取用户信息，得到用户身份。
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户{}不存在", userId);
            Boolean isVip = false;
            //0:普通用户  1:VIP会员 当vip等于1时并且过期时间大于当前时间，则vip等于true
            //但是对于Date类型，不能直接写>,需要使用after函数来进行比较
            if (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVip = true;
            }
            //假设不需要进行进一步的判断。
            Boolean needCheck = false;

            //5.2 情况二：普通用户收听资源是VIP免费，需要进一步获取用户购买情况进行下一步判断。
            if (!isVip && SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
                //满足条件，需要进一步的判断
                needCheck = true;
            }


            //5.3 情况三：所有用户收听资源是付费，需要进一步获取用户购买情况进行下一步判断。
            if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //满足条件，需要进一步的判断
                needCheck = true;
            }


            //5.4 判断当前页中声音是否需要将付费标识改为true，如果经过查询购买状态未付费则将付费标识改为true。
            if (needCheck) {
                //5.4.1 获取本页中需要检查购买状态的声音id列表(去掉试听的声音id)
                List<Long> needCheckPayStatusTrackIdList = pageInfo.getRecords()
                        .stream()
                        //去掉试听的部分
                        .filter(t -> t.getOrderNum() > tracksForFree)
                        //这里只需要专辑id即可
                        .map(AlbumTrackListVo::getTrackId)
                        .collect(Collectors.toList());

                //5.4.2 远程调用用户服务获取本页中声音的购买状态。
                Map<Long, Integer> payResultMap = userFeignClient.userIsPaidTrack(userId, albumId, needCheckPayStatusTrackIdList).getData();

                /*
                5.4.3 判断本页中声音在购买结果中是0，则说明未购买，
                付费标识改为true，试听的去除。
                 */
                pageInfo.getRecords()
                        .stream()
                        .filter(track -> track.getOrderNum() > tracksForFree)
                        //如果购买状态等于0，则说明喂狗改，将付费标识设置为true
//                        .forEach(track -> track.setIsShowPaidMark(payResultMap.get(track.getTrackId()) == 0));
                        //这句话等价于下面这句话
                        .forEach(track -> {
                            Integer payStatus = payResultMap.get(track.getTrackId());
                            if (payStatus.intValue() == 0) {
                                track.setIsShowPaidMark(true);
                            }
                        });

            }

        }

        return pageInfo;
    }

    /**
     * 更新数据库中声音表跟专辑表的统计信息
     *
     * @param trackStatMqVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackStat(TrackStatMqVo trackStatMqVo) {
        //1.更新声音表统计信息
        LambdaUpdateWrapper<TrackStat> statLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        statLambdaUpdateWrapper.eq(TrackStat::getTrackId, trackStatMqVo.getTrackId())
                .eq(TrackStat::getStatType, trackStatMqVo.getStatType())
                //设置set部分: stat_num = stat_num + ?
                .setSql("stat_num = stat_num + " + trackStatMqVo.getCount());
        trackStatMapper.update(null, statLambdaUpdateWrapper);

        //2.更新所属专辑统计信息：播放量、评论量 需要被同时更新。
        if (SystemConstant.TRACK_STAT_PLAY.equals(trackStatMqVo.getStatType())) {
            //更新所属专辑播放量
            this.updateAlbumStat(
                    trackStatMqVo.getAlbumId(),
                    SystemConstant.ALBUM_STAT_PLAY,
                    trackStatMqVo.getCount());
        }

        if (SystemConstant.TRACK_STAT_COMMENT.equals(trackStatMqVo.getStatType())) {
            //更新所属专辑评论量
            this.updateAlbumStat(
                    trackStatMqVo.getAlbumId(),
                    SystemConstant.ALBUM_STAT_COMMENT,
                    trackStatMqVo.getCount());
        }

    }

    /**
     * 获取声音的统计信息
     *
     * @param trackId
     * @return
     */
    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {
        return trackInfoMapper.getTrackStatVo(trackId);
    }

    /**
     * 根据待购声音作为起始标准查询当前用户未购买的声音列表
     *
     * @param userId
     * @param trackId
     * @param trackCount 限制购买的数量
     * @return
     */
    @Override
    public List<TrackInfo> findPaidTrackInfoList(Long userId, Long trackId, Integer trackCount) {
        //1.根据待购声音id获取声音信息，得到声音序号以及专辑id
        TrackInfo trackInfo = baseMapper.selectById(trackId);
        Integer orderNum = trackInfo.getOrderNum();
        Long albumId = trackInfo.getAlbumId();

        //2.远程调用用户服务获取该专辑下用户已购的声音id列表
        List<Long> userPaidTrackIdList = userFeignClient.findUserPaidTrackIdList(userId, albumId).getData();

        /*
        3.查询专辑下的声音列表
        条件：
        1.专辑id(等值)
        2.声音序号(大于等于)
        3.排除已购声音id(不在这个区间内)
        4.排序(按照序号升序排序)
        5.限制购买数量
         */
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, albumId)
                .ge(TrackInfo::getOrderNum, orderNum)
                //排除掉已购的声音id
                .notIn(CollectionUtil.isNotEmpty(userPaidTrackIdList), TrackInfo::getId, userPaidTrackIdList)
                .orderByAsc(TrackInfo::getOrderNum)
                .last("limit " + trackCount)
                //只查询这四个字段，防止用户拿到播放地址，直接收听专辑
                .select(
                        TrackInfo::getId,
                        TrackInfo::getTrackTitle,
                        TrackInfo::getCoverUrl,
                        //在订单服务中需要获取声音的单价，而这需要根据专辑id来查询
                        TrackInfo::getAlbumId
                );
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);

        return trackInfoList;

    }

    /**
     * 更新指定专辑统计信息
     *
     * @param albumId  专辑id
     * @param statType 统计类型
     * @param statNum  增量统计数值
     */
    private void updateAlbumStat(Long albumId, String statType, Integer statNum) {
        //1.更新声音表统计信息
        LambdaUpdateWrapper<AlbumStat> statLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        statLambdaUpdateWrapper.eq(AlbumStat::getAlbumId, albumId)
                .eq(AlbumStat::getStatType, statType)
                //设置set部分: stat_num = stat_num + ?
                .setSql("stat_num = stat_num + " + statNum);
        albumStatMapper.update(null, statLambdaUpdateWrapper);

    }


}
